﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Wombat.Core;
using Wombat.Core.Gates;
using Wombat.Data.Model.Dtos;
using Wombat.WcfClientGate;

namespace Wombat.Bootstrappers.Essentials
{
    public partial class Bootstrap
    {
        private StorageBaseGate _floatStorageGate;
        private IObserverPort<ITagValue> _floatStorageGatePort;
        private ServiceEndpointBaseGate _webServiceGate;
        private IObservablePort _webServiceGatePort;
        private IList<CodeExecuterGate> _codeExecuterGates;
        private readonly List<IDisposable> _subscriptions = new List<IDisposable>();
        private WcfExportGate _wcfExportGate;
        private IObserverPort<ITagValue> _wcfExportGatePort;

        private void LoadPersistanceGate()
        {
            _floatStorageGate = _floatStorageGateFactory();
            _floatStorageGate.Initialize();
            _floatStorageGatePort = (IObserverPort<ITagValue>) _floatStorageGate.Ports.First(p => p.Name == "DataIn");
        }

        private async Task LoadWebServiceGate()
        {
            _webServiceGate = _serviceEndpointGateFactory();
            await Task.Factory.StartNew(() => _webServiceGate.Initialize());
            _observationManager.Register<ITagValue>(_webServiceGate);
            _webServiceGatePort = (IObservablePort) _webServiceGate.Ports.First(p => p.Name == "ServiceEndPointPort");
        }

        private async Task LoadAndWireAllKpiGates()
        {
            var tags = await _dataService.GetCodeTagsAsync();
            _codeExecuterGates =
                tags.Select(GetCodeExecuterGate).ToList();
        }

        private CodeExecuterGate GetCodeExecuterGate(CodeTagDto tagDto)
        {
            var codeExeGate = _codeBlockExecuterGateFactory();
            codeExeGate.Code = tagDto.Code;
            ((IUniqueIdentifiableSetter) codeExeGate).SetGuid(tagDto.Guid);
            var portsToGates =
                (from inputParameter 
                    in tagDto.InputParameters 
                    let portId = ((ITagToVariableMapper) codeExeGate).AddMapping(inputParameter.TagGuid, inputParameter.VariableName)
                    select Tuple.Create(portId, inputParameter.TagGuid))
                .ToList();
            codeExeGate.Initialize();

            //Wire every input gate to the web service listener
            //Note that we do not currently support wiring a gate to another gate 
            //For example where a code gate uses another code gate as input.
            foreach (var portToGate in portsToGates)
            {
                var port = codeExeGate.Ports.OfType<IObserverPort<ITagValue>>().First(p => p.Guid == portToGate.Item1);
                _subscriptions.Add(_webServiceGate.Subscribe(string.Format(@"{{{0}}}", portToGate.Item2), port));
            }
            var outPutGate = codeExeGate.Ports.OfType<IObservablePort>().First();
            _observationManager.Register<ITagValue>(codeExeGate);

            //Wire the output gate to the float exe and wcfExport gates
            WireToGlobalListeners(outPutGate);
            return codeExeGate;
        }

        private void WireToGlobalListeners(IObservablePort outPutGate)
        {
            outPutGate.Subscribe("*", _floatStorageGatePort);
            outPutGate.Subscribe("*", _wcfExportGatePort);
        }

        private async Task LoadWcfExportGate()
        {
            _wcfExportGate = _wcfExportGateFactory();
            ((IInitializable)_wcfExportGate).Initialize();
            await _wcfExportGate.StartAsync();
            _wcfExportGatePort = _wcfExportGate.Ports.OfType<IObserverPort<ITagValue>>().First();
        }

        private async Task StartAndWireWebServiceGate()
        {
            WireToGlobalListeners(_webServiceGatePort);
            await _webServiceGate.StartAsync();
        }
    }
}
